<head><meta name="viewport" content="user-scalable=no,initial-scale=1,maximum-scale=1">
<meta charset=utf-8>
<style>
body { margin: 0px; }
canvas { width:100%; height:100%; overflow: hidden; }
</style>
<script type="text/javascript" src="../h3du_min.js"></script>
<script type="text/javascript" src="../extras/camera.js"></script>
<script type="text/javascript" src="../extras/frame.js"></script>
<script type="text/javascript" src="demoutil.js"></script>
<script type="text/javascript" src="../extras/meshjson.js"></script>
<script type="text/javascript" src="expressions.js"></script>
</head>
<body>
<div style="position:absolute;left:0;top:1em">
<span>X:</span> <input type=text id=xcoord value="cos(v)*cos(u)"><br>
<span>Y:</span> <input type=text id=ycoord value="sin(v)*cos(u)"><br>
<span>Z:</span> <input type=text id=zcoord value="sin(u)"><br>
<span>MinU:</span> <input type=text id=minu value="-pi/2"><br>
<span>MaxU:</span> <input type=text id=maxu value="pi/2"><br>
<span>MinV:</span> <input type=text id=minv value="-pi"><br>
<span>MaxV:</span> <input type=text id=maxv value="pi"><br>
<a href="javascript:formulaEditorHelp()">Syntax help</a><br>
Source code for the parametric<br>
surface object. Use this object as<br>
a parameter in the <code>evalSurface</code> method
of <code>SurfaceBuilder</code>, example:<br>
<code>var surface = new H3DU.SurfaceBuilder()<br>
.vertex({"evaluate":...});</code>.<br>
<textarea id=sourceCode rows=5 cols=25>
</textarea>
<div id="settings"></div>
</div>
<canvas id=canvas></canvas>
<script id="demo">
/* global H3DU, addRange, getExpression, makeMesh, normalCalcExpr, updateShape */
// <!--
/*
 Any copyright to this file is released to the Public Domain.
 http://creativecommons.org/publicdomain/zero/1.0/
 If you like this, you should donate
 to Peter O. (original author of
 the Public Domain HTML 3D Library) at:
 http://peteroupc.github.io/
*/
if(typeof Math.sign === "undefined") {
  Math.sign = function(x) {
    "use strict";
    return x < 0 ? -1 : x === 0 ? 0 : 1;
  };
}
function makeMeshDoubleSided(func, resolution) {
  "use strict";
  var mesh = makeMesh(func, resolution, resolution);
  var otherWinding = new H3DU.MeshBuffer().merge(mesh).reverseWinding().reverseNormals();
  return mesh.merge((otherWinding));
}

function CustomSurface(x, y, z, minu, maxu, minv, maxv) {
  "use strict";
  var ranges = [
    getExpression(minu).constantValue(),
    getExpression(maxu).constantValue(),
    getExpression(minv).constantValue(),
    getExpression(maxv).constantValue()];
  if(ranges[0] === null || ranges[1] === null ||
  (typeof ranges[2] === "undefined" || ranges[2] === null) || ranges[3] === null) {
    throw new Error("min/max must be a constant");
  }
  if(isNaN(ranges[0]) || isNaN(ranges[1]) ||
  isNaN(ranges[2]) || isNaN(ranges[3])) {
    throw new Error("min/max must be a constant");
  }
  var xe = getExpression(x);
  var ye = getExpression(y);
  var ze = getExpression(z);
  this.ranges = ranges.slice(0, 4);
  this.endPoints = function() {
    return this.ranges.slice(0, 4);
  };
  var code = "" +
  "return [" +
  " " + getExpression(x).toJSString() + "," +
  " " + getExpression(y).toJSString() + "," +
  " " + getExpression(z).toJSString() + "];\n";
  var normalExpr = normalCalcExpr([xe, ye, ze]);
  var normalCode = "" +
  "return [" +
  " " + normalExpr[0].toJSString() + "," +
  " " + normalExpr[1].toJSString() + "," +
  " " + normalExpr[2].toJSString() + "];\n";
  this.getCode = function() {
    return "var evaluator=new H3DU.SurfaceBuilder().positionNormal({\n" +
    "\"evaluate\":function(u, v) {\n" + code + "},\n" +
    "\"gradient\":function(u, v) {\n" + normalCode + "},\n" +
    "\"endPoints\":function() {\n return [" + ranges[0] + "," + ranges[1] +
       "," + ranges[2] + "," + ranges[3] + "]; }})\n";
  };
  this.evaluate = null;
  try {
    // NOTE: The code variable was generated from parsed and
    // validated expressions, with a limited set of supported
    // functions rather than arbitrary JavaScript code,
    // so we disable the "no-new-func" rule here.
    // eslint-disable-next-line no-new-func
    this.evaluate = new Function("u", "v", code);
  } catch(ex) {
    this.evaluate = null;
  }
}

var shapeGroup = new H3DU.ShapeGroup();
var allsettings = {
  "current":0,
  "custom-x":"cos(v)*cos(u)",
  "custom-y":"sin(v)*cos(u)",
  "custom-z":"sin(u)",
  "custom-minu":"-pi/2",
  "custom-maxu":"pi/2",
  "custom-minv":"pi",
  "custom-maxv":"-pi"
};

function updateMesh() {
  "use strict";
  switch(allsettings.current) {
  case 0:
    var cs = new CustomSurface(
      allsettings["custom-x"],
      allsettings["custom-y"],
      allsettings["custom-z"],
      allsettings["custom-minu"],
      allsettings["custom-maxu"],
      allsettings["custom-minv"],
      allsettings["custom-maxv"]
   );
    if(!cs.evaluate)return new H3DU.MeshBuffer();
    document.getElementById("sourceCode").value = cs.getCode();
    return makeMeshDoubleSided(cs, 70);
  default:
    return new H3DU.MeshBuffer();
  }
}

function validateExpr(value) {
  "use strict";
  try {
    getExpression(value).toJSString();
    return true;
  } catch(ex) {
    return false;
  }
}
function validateConst(value) {
  "use strict";
  try {
    if(getExpression(value).constantValue() === null) {
      return false;
    }
    return true;
  } catch(ex) {
    return false;
  }
}
/* exported pushSetting */
function pushSetting(ranges, name, label, min, max, step) {
  "use strict";
  ranges.push(addRange(label, min, max, step, allsettings[name], function(val) {
    allsettings[name] = val;
    updateShape(updateMesh, allsettings, shapeGroup);
  }));
}
function validateListener(id, key) {
  "use strict";
  var func = function(e) {
    if(validateExpr(e.target.value)) {
      allsettings[key] = e.target.value;
      updateShape(updateMesh, allsettings, shapeGroup);
    }
  };
  allsettings[key] = document.getElementById(id).value;
  document.getElementById(id).addEventListener("input", func);
  document.getElementById(id).addEventListener("change", func);
}
function validateConstListener(id, key) {
  "use strict";
  var func = function(e) {
    if(validateConst(e.target.value)) {
      allsettings[key] = e.target.value;
      updateShape(updateMesh, allsettings, shapeGroup);
    }
  };
  allsettings[key] = document.getElementById(id).value;
  document.getElementById(id).addEventListener("input", func);
  document.getElementById(id).addEventListener("change", func);
}
function link0() {
  "use strict";
  var exprs = ["xcoord", "ycoord", "zcoord"];
  var ekeys = ["custom-x", "custom-y", "custom-z"];
  var consts = ["minu", "minv", "maxu", "maxv"];
  var ckeys = ["custom-minu", "custom-minv", "custom-maxu", "custom-maxv"];
  for(var i = 0; i < exprs.length; i++) {
    validateListener(exprs[i], ekeys[i]);
  }
  for(i = 0; i < consts.length; i++) {
    validateConstListener(consts[i], ckeys[i]);
  }
  updateShape(updateMesh, allsettings, shapeGroup);
}

  // Create the 3D scene; find the HTML canvas and pass it
  // to Scene3D.
var scene = new H3DU.Scene3D(document.getElementById("canvas"));
var input = new H3DU.InputTracker(scene.getCanvas());
scene.setClearColor("white");
scene.cullFace(H3DU.Scene3D.FRONT);
var sub = new H3DU.Batch3D();
var camera = new H3DU.Camera(sub, 45, 1, 100);
camera.setDistance(5);
var pc = new H3DU.FrameCounterDiv();
link0();
sub.addShape(shapeGroup);
H3DU.renderLoop(function() {
  "use strict";
  pc.update();
  camera.update(input.update());
  sub.getLights().setDirectionalLight(0, camera.getPosition());
  scene.render(sub);
});
// -->
</script>
</body>
